package com.miuugo.common.tools;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.springframework.cglib.beans.BeanCopier;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 通过json序列化及反系列化copy属性信息
 * @author miuu
 */
public class BeanUtils {

    @SuppressWarnings("all")
    public static <T> T copyProperties(Object source, Class<T> clazz) {
        if (source == null) {
            throw new RuntimeException("source object is null!");
        }

        //SerializerFeature.DisableCircularReferenceDetect 解决循环引用的问题
        String jsonStr = JSON.toJSONString(source, SerializerFeature.DisableCircularReferenceDetect);
        return JSON.parseObject(jsonStr, clazz);
    }

    @SuppressWarnings("all")
    public static <T> T copyProperties(Object source, TypeReference<T> type) {
        if (source == null) {
            throw new RuntimeException("source object is null!");
        }

        String jsonStr = JSON.toJSONString(source, SerializerFeature.DisableCircularReferenceDetect);
        return JSON.parseObject(jsonStr, type);
    }

    static final Map<String, BeanCopier> BEAN_COPIERS = new HashMap<>();

    public static void fastBeanCopy(Object source, Object target) {
        if (source == null || target == null) {
            return;
        }
        String key = genKey(source, target);
        BeanCopier copier = null;
        if (!BEAN_COPIERS.containsKey(key)) {
            copier = BeanCopier.create(source.getClass(), target.getClass(), false);
            BEAN_COPIERS.put(key, copier);
        } else {
            copier = BEAN_COPIERS.get(key);
        }
        copier.copy(source, target, null);
    }

    public static <S, T> T fastBeanCopy(S source, Class<T> targetClazz) {
        if (source == null) {
            return null;
        }
        T target = null;
        try {
            target = targetClazz.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        fastBeanCopy(source, target);
        return target;
    }

    private static String genKey(Object source, Object target) {
        return source.getClass().getName() + target.getClass().getName();
    }

    //把Map转化为JavaBean
    public static <T> T map2bean(Map<String, Object> map, Class<T> clz) {
        //创建一个需要转换为的类型的对象
        T obj = null;
        try {
            obj = clz.newInstance();
            //从Map中获取和属性名称一样的值，把值设置给对象(setter方法)
            //得到属性的描述器
            BeanInfo b = Introspector.getBeanInfo(clz, Object.class);
            PropertyDescriptor[] pds = b.getPropertyDescriptors();
            for (PropertyDescriptor pd : pds) {
                //得到属性的setter方法
                Method setter = pd.getWriteMethod();
                //得到key名字和属性名字相同的value设置给属性
                setter.invoke(obj, map.get(pd.getName()));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }

    //把JavaBean转化为map
    public static Map<String, Object> bean2map(Object bean) {
        Map<String, Object> map = new HashMap<>();
        //获取JavaBean的描述器
        BeanInfo b = null;
        try {
            b = Introspector.getBeanInfo(bean.getClass(), Object.class);
            //获取属性描述器
            PropertyDescriptor[] pds = b.getPropertyDescriptors();
            //对属性迭代
            for (PropertyDescriptor pd : pds) {
                //属性名称
                String propertyName = pd.getName();
                //属性值,用getter方法获取
                Method m = pd.getReadMethod();
                Object properValue = m.invoke(bean);//用对象执行getter方法获得属性值

                //把属性名-属性值 存到Map中
                map.put(propertyName, properValue);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

}
